/*
 * File: PhidgetService.java
 * 
 * Change Log
 * Author				DTG				Description of Change
 * ---------------------------------------------------------------------------
 * Timothy M. Lemm		17OCT11 2143	Initial Service Structure
 * 										Added binding to Core Service 	
 */

package com.erau.droidsatframework.phidget;

import java.util.HashMap;
import java.util.Iterator;

import com.erau.droidsatframework.control.ControlService;
import com.erau.droidsatframework.control.ControlService.ControlBinder;
import com.erau.droidsatframework.core.CoreService;
import com.erau.droidsatframework.core.USB_CONSTANTS;
import com.erau.droidsatframework.core.CoreService.CoreBinder;

import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.hardware.usb.*;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

public class PhidgetService extends Service {

	private final IBinder localBinder = new PhidgetBinder(); //For dealing with incoming bind from control 
	CoreService core;					//Local handle of the CoreService Instance for binding
	private boolean isBound = false;	//Status indicator of being bound or not to the core
	
	private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
	
	
	private byte[] fullLeft = {00, 66, 02, 00, 00, 00, 00, 00}; // See wiki for the source of these values
	private byte[] fullRight = {00, 66, 02, 00, 00, 00, 00, 00};
	private byte[] stop = {00, 00, 00, 00, 00, 00, 00, 00};
	
	private UsbManager usbManager;
	private UsbDevice  phidget;
	private UsbInterface phidgetInt;
	public UsbEndpoint phidgetEndPoint;
	private UsbDeviceConnection phidgetConnection;
	
	private PendingIntent phigetPermissionIntent;
	
	private boolean phidgetConnected = false;
	private boolean connectionActive = false;
	
	
	@Override
	public IBinder onBind(Intent intent) {
		//Phidget does not get bound by anything as of now so return null
		return localBinder;
	}
	
	public class PhidgetBinder extends Binder {
		public PhidgetService getService() {
			return PhidgetService.this;
		}//End getService
	}//End ControlBincer
	
	@Override
	public void onCreate() {
		Toast.makeText(this, "Phidget is created", 2000).show();
		//Bind Back to Core...
		Intent phidget2CoreIntent = new Intent(this, CoreService.class); //Intent to bind
		bindService(phidget2CoreIntent, bindConnection, Context.BIND_AUTO_CREATE ); //Bind
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		return START_NOT_STICKY; //We can die
	}//End onStartCommand
	
	@Override
	public void onDestroy() {
		commandProcess(0,0,1);
		phidgetConnection.releaseInterface(phidgetInt);
		phidgetConnection.close();
		Log.v("Phidget", "Phidget Destroy Called");
		//Toast.makeText(this, "Game over man... game over (phidget)", 2000).show();
	}
	
	//CLASS EXTENSION: bindConnection
	//Required class extension of Service Connection to provide a call 
	//back for the system to pass the bound interface back
	private ServiceConnection bindConnection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			CoreBinder coreBinder = (CoreBinder) service; //cast the call back service into the core binder
			core = coreBinder.getService();	//bring in the active instance of the core service
			isBound = true;		//update status flag
			deviceInit();
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			isBound = false; //update status flag
			
		}
	};
	
	private void deviceInit(){
		//Establish the intent filter and reciever for the permissions stuff...
		phigetPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
		IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
		registerReceiver(phidgetPermissionReceiver, filter);
		
		//Establish the manager
		usbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
		
		//Find the phidget board out of the sea of devices!
		HashMap<String, UsbDevice> foundDevices = usbManager.getDeviceList();
		Iterator<UsbDevice> iterator = foundDevices.values().iterator();
		while(iterator.hasNext()){
			//We are assuming that the phidget board is the only thing that is connected... not exactly
			//good practice but... it is what it is...
			phidget = iterator.next();
			if(phidget != null){
				phidgetConnected = true;
			}
		}
		
		//As long as we found the phidget proceed...
		if(phidgetConnected){
			phidgetInt = phidget.getInterface(0);
			
			phidgetEndPoint = phidgetInt.getEndpoint(0);
			
			//Now we check if we have permission to communicate with the phidget board...
			if(usbManager.hasPermission(phidget)){
				afterPermissionGranted();
			}
			else{
				usbManager.requestPermission(phidget, phigetPermissionIntent);
			}
		}
		
		
	}
	
	private void afterPermissionGranted(){
		phidgetConnection = usbManager.openDevice(phidget);
		if(phidgetConnection != null){
			if(phidgetConnection.claimInterface(phidgetInt, true)){
			connectionActive = true;
			}
		}
	}
	
	public void commandProcess(int velocity, int acceleration, int axis){
		if(connectionActive){
			//TODO make this command processor a little more advaced.... right now we are using hard coded values... need to make dynamic
			////Toast.makeText(this, "Velocity: "+velocity, 2000).show();
			byte[] array=PhidgetService.Convert(velocity, acceleration, axis);
			
			phidgetConnection.controlTransfer(USB_CONSTANTS.TYPE_HID, 0x9, USB_CONSTANTS.USB_2p0, 0,array, array.length, 0);			
			
		}	
	}
	
	private final BroadcastReceiver phidgetPermissionReceiver = new BroadcastReceiver(){

		@Override
		public void onReceive(Context arg0, Intent arg1) {
			String action = arg1.getAction();
			if(ACTION_USB_PERMISSION.equals(action)){
				synchronized (this) {
					if(arg1.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)){
						afterPermissionGranted();
					}
					else{
						//log no permission
					}
				}
			}
		}	
	};// permission receiver

	public static byte[] Convert(int velocity, int acceleration, int motor) {
		//Velocity Value between -1000 and 1000
		//Acceleration Value between 1 and 100
		//Motor Value is either 0 or 1
		float newVelocity;
		float calcVelocity;
		int phidgetVelocity; 
		String hexVelocity;
		float lastVelocity=0;
		
		float calcAccel;
		int phidgetAccel;
		String hexAccel="0";
		float lastAccel=1;
		
		String hexMotor;
		
		int hex1, hex2, hex3, hex4;
		String hexstring;
		int length;
		
		newVelocity=(float) (velocity/10.0);
		
		if (velocity<0 && velocity >=-1000) {
			calcVelocity=(float) (1.269607*(Math.round(newVelocity))+255.975152);
		}
		else if (velocity<=1000 && velocity >=0) {
			calcVelocity=(float) (1.269621*(Math.round(newVelocity)) + 0.023879);
		}
		else {
			calcVelocity= lastVelocity;
		}
		
		lastVelocity=calcVelocity;
		
		if (motor!=0 && motor!=1) {
			//Default motor to port 0
			motor=0;
		}
		
		hexMotor=Integer.toHexString(motor);
		hex1=Integer.parseInt("0".concat(hexMotor),16);
		
		if (acceleration>=1 && acceleration <=100) {
			calcAccel=(float) (acceleration*10.23);
		}
		else {
			calcAccel=lastAccel;
		}
		
		lastAccel=calcAccel;
		
		phidgetAccel=Math.round(calcAccel);
		
		phidgetVelocity=Math.round(calcVelocity);

		hexVelocity=Integer.toHexString(phidgetVelocity).toUpperCase();
		hexstring=Integer.toHexString(phidgetAccel).toUpperCase();
		
		hex2=Integer.parseInt(hexVelocity,16);
		
		length=hexstring.length();
		
		switch (length) {
		case 1: hexAccel="000".concat(hexstring);  break;
		case 2: hexAccel="00".concat(hexstring);  break;
		case 3: hexAccel="0".concat(hexstring);  break;
		}
		
		hex3=Integer.parseInt(hexAccel.substring(0,2),16);
		hex4=Integer.parseInt(hexAccel.substring(2,4),16);
		
		byte[] phidgetValue={(byte) hex1, (byte) hex2, (byte) hex3, (byte) hex4, 00, 00, 00, 00};
		
		return phidgetValue;  
	} 
}
